Skip to content

feat(cloud-registry): verifications - #578

Open
uniqueg wants to merge 6 commits into
devfrom
feat/459-verifications
Open

uniqueg wants to merge 6 commits into
devfrom
feat/459-verifications

Conversation

@uniqueg

@uniqueg uniqueg commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Apps can show and manage Cloud Registry verifications (cloud-registry 0.4.0 and later) without their own copy of the provider.

Provider (@elixir-cloud/cloud-registry/providers)

  • getVerifications(), getServiceVerification(id), verifyService(id, verification?) and deleteServiceVerification(id) for GET /verifications and GET/PUT/DELETE /services/{id}/verification, matching the registry's OpenAPI (additions.openapi.yaml). They are optional in CloudRegistryProvider, so custom providers without them still conform.
  • Types Verification, ServiceVerification and VerificationRegister.
  • Helpers matchesVerification() and verifiedServices(): a verification only counts while the service still has the verified URL (as written) and API type (group and artifact, trimmed and case-insensitive, as the registry compares them with Python's strip().casefold()). They are ported from the Cloud Registry UI's lib/verifications.ts.
  • CloudRegistryError.code: the problem code of a conflict, e.g. verification-mismatch or pending-service.

Component ecc-client-elixir-cloud-registry-service-verification

  • Shows a "Verified" badge (the tooltip gives the date) for verified services, and nothing for other services.
  • Has an admin property: admins see "Not verified", "Verify" and "Remove verification". "Verify" sends the URL and API type of service as the checked values, so a service that changed in the meantime isn't verified (409).
  • Has a verification property for lists: the app loads all verifications with one request and passes each row's verification, so the rows make no requests of their own.
  • Reacts to provider or baseUrl set after the first update, the behaviour fix: react to provider set after the first update #565 asks for in other components. It ignores stale answers, tracks running verify and remove requests per service, and still reports a result that arrives after a reload, but shows it only for the same service of the same registry.
  • Fires ecc-service-verification-changed and ecc-service-verification-failed. The failure event carries the action, status, problem details and conflict code, the same way the service-create events carry them.

Docs: a new component page, a Verifications section and types in the providers page, the code property in the Errors table, and a link in the package introduction.

Verified badge in the services list: the issue asks for it, but the services list (ecc-client-ga4gh-service-registry-services) lives in the service-registry package. #455 is reworking that list (filters, the verified filter, a per-row render hook), and #565 also changes it. This PR therefore doesn't touch the list. Apps put the component in a row's actions-<id> slot, or in their own list, with the preloaded verification. The docs show this. Once #455 adds its row hook, the badge can go there without any change to this component.

Closes #459

Verification

All commands were run in packages/ecc-client-elixir-cloud-registry unless noted.

  • npm run test -- --coverage: 95 tests passed in Chromium and Firefox. WebKit can't run on this machine: Playwright doesn't support its OS (Ubuntu 26.04), so CI covers WebKit. Line coverage of the new code: verifications.ts 100%, service-verification.ts 450/454.
  • npm run build: passes, and generates the component, its React wrapper and the custom elements manifest.
  • npm run lint at the repo root (husky pre-commit, all packages): 0 errors.
  • npx tsc -p tsconfig.prod.json: no errors.
  • npm run build in apps/documentation: passes, and the new page renders as /docs/cloud-registry/components/service-verification.
  • npx cspell on the cloud-registry docs pages: 0 issues.
  • CI: all checks pass (Lint, Test on Node 22 and 24 including WebKit, CodeQL, Codecov patch and project, Read the Docs preview)
  • Review: adversarial-reviewer and quality-reviewer, in two rounds. Fixed in round 1:
    • a reload during verify or remove dropped the result;
    • a stale preloaded verification came back after a reload;
    • case folding of U+1E9E and U+0131 differed from Python's;
    • code was missing on CloudRegistryError;
    • the intro link was missing;
    • tests were missing for the busy, race and missing-method branches. Round 2 (both reviewers): running requests now block only their own service, and results from another registry are no longer shown (also when kept over the app's verification). It also adds tests for removing a verification of a previous service, and docs notes on reloading and on passing stable values.
  • Sourcery (8 threads): 3 fixed. The component now loads again when the URL or API type of the service changes and after unclear verify or remove failures, and running requests are keyed by registry and service. 5 were answered with reasons in their threads (registry identity in the event, app-supplied values across registries, ordering between tabs, keeping results without verification, the badge in the services list). CI was green again after the fixes.

Declined review findings

  • Duplicated failureDetail (quality, medium). It is similar to the one in service-create. Sharing it means refactoring service-create, which is outside this issue, and other open work touches the package's components. A shared helper in providers/errors.ts would fit a follow-up.
  • Use trim().toLowerCase() instead of emulating casefold() (quality, low). The client should agree with the registry, which compares with strip().casefold(), and the Cloud Registry UI already uses this approach. The reviewer's brute-force check found only the two exceptions that this PR now handles.
  • Docs version 2.0.0-alpha.51 (quality, low). This is the next release, the first one that contains the component. Other pages give the version that introduced their component.
  • Test for a load that is still running when a verify or remove finishes (quality, round 2). The UI can't reach this: while a load runs, the component renders no buttons. The loadRequest bump in applied() stays as a safeguard, with a comment.

Open questions

None.

🤖 Generated with Claude Code

uniqueg and others added 5 commits November 13, 2026 08:13
Add provider methods and types for the verification endpoints of
Cloud Registry 0.4.0 and later (GET /verifications, GET, PUT and
DELETE /services/{id}/verification), optional in the interface so
that custom providers still conform, and helpers that tell whether a
verification still matches its service, as the registry compares
them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add ecc-client-elixir-cloud-registry-service-verification: a badge
for verified services, and "Verify" and "Remove verification" for
admins. It sends the URL and API type of the service shown as the
ones checked, takes a verification the app has loaded already (e.g.
for a list, from getVerifications()), reacts to a provider set after
the first update, and fires change and failure events with the
status and conflict code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add code to CloudRegistryError, so that apps and the verification
component read the conflict code of a 409 without digging into the
problem. Test the component's busy state, races and failures of
verify and remove, and link it from the package introduction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reload while a verify or remove ran (e.g. a new provider after a
token refresh) dropped the result: no change event, the old state
shown, and the button enabled again. Track loads apart from verify
and remove, always report a result the registry has applied, and
show it until the app sets verification again. Fold capital sharp s
and dotless i as Python does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Track running verify and remove requests by service ID, so that one
for a previous service doesn't block the service shown, and show a
result (also one kept over the app's verification) only while the
component shows the same service of the same registry (base URL).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Data structures

Define the verification data types and optional provider contract, and preserve conflict codes on registry errors so callers can interpret verification failures.

  • packages/ecc-client-elixir-cloud-registry/src/providers/cr-provider.ts L83-114, L139-148
  • packages/ecc-client-elixir-cloud-registry/src/providers/errors.ts L100-105, L117

Expose the verification event detail types so applications can type-check the component’s changed and failed events.

  • packages/ecc-client-elixir-cloud-registry/src/events/ecc-service-verification-changed.ts L1-14
  • packages/ecc-client-elixir-cloud-registry/src/events/ecc-service-verification-failed.ts L1-27
  • packages/ecc-client-elixir-cloud-registry/src/events/index.ts L5-6

API

Add the REST provider operations for listing, fetching, creating, and deleting service verifications, including encoded service IDs and registry errors.

  • packages/ecc-client-elixir-cloud-registry/src/providers/rest-cr-provider.ts L8-10, L207-305

Export the verification types and comparison helpers through the providers package entry point.

  • packages/ecc-client-elixir-cloud-registry/src/providers/index.ts L21-26

Register and export the service-verification custom element as a public component.

  • packages/ecc-client-elixir-cloud-registry/src/components/index.ts L2
  • packages/ecc-client-elixir-cloud-registry/src/components/service-verification/index.ts L1-15

Implementation

Implement verification matching and list filtering using the registry’s URL and Python-style type comparison rules.

  • packages/ecc-client-elixir-cloud-registry/src/providers/verifications.ts L1-100

Implement component loading, admin actions, stale-request handling, failure reconciliation, event dispatch, and rendering for service verification.

  • packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts L1-494

Documentation

Document component usage, provider methods, matching helpers, types, errors, properties, events, and styling, and link the feature from the Cloud Registry introduction.

  • apps/documentation/pages/docs/cloud-registry/components/_meta.json L2-3
  • apps/documentation/pages/docs/cloud-registry/components/service-verification.mdx L1-194
  • apps/documentation/pages/docs/cloud-registry/introduction.mdx L23
  • apps/documentation/pages/docs/cloud-registry/providers.mdx L36-41, L164-205, L309-330 (+1 small edits)

Tests

Test REST verification requests, matching semantics, and filtering of verified services.

  • packages/ecc-client-elixir-cloud-registry/src/providers/tests/rest-cr-provider.test.ts L35-41
  • packages/ecc-client-elixir-cloud-registry/src/providers/tests/verifications.test.ts L1-258

Exercise component rendering, actions, errors, provider changes, and request races.

  • packages/ecc-client-elixir-cloud-registry/src/components/service-verification/tests/service-verification.test.ts L1-823

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.34025% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.55%. Comparing base (1fa86b8) to head (5614d12).
⚠️ Report is 1 commits behind head on dev.

Files with missing lines Patch % Lines
...nents/service-verification/service-verification.ts 97.57% 11 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #578      +/-   ##
==========================================
+ Coverage   71.40%   75.55%   +4.15%     
==========================================
  Files          23       36      +13     
  Lines        5655     8846    +3191     
  Branches      199      470     +271     
==========================================
+ Hits         4038     6684    +2646     
- Misses       1610     2152     +542     
- Partials        7       10       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@uniqueg
uniqueg marked this pull request as ready for review November 13, 2026 13:26
@uniqueg
uniqueg requested a review from anuragxxd as a code owner November 13, 2026 13:26
@sourcery-ai

sourcery-ai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Linked issues

Checked at commit 5614d12

#459 feat(cloud-registry): verifications · 2 of 3 met

# Requirement Status Notes
1 Provider methods for the verification endpoints. ✅ Met The REST provider implements the list, get, verify, and delete operations, and the corresponding methods are optional in CloudRegistryProvider.
2 A verified badge in the services list. 🟡 Partly met The PR adds a standalone verification component that can be placed in a list row, but says it “doesn't touch the list”; the list itself is not wired to show verification badges.
3 Apps can show and manage verifications without their own copy of the provider. ✅ Met The shared REST provider and new component let apps load, display, verify, and remove verifications.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 8 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="301" />
<code_context>
+        this.own = { id, verification, provider };
+      }
+    }
+    this.changed(id, verification);
+  }
+
</code_context>
<issue_to_address>
**Old registry results corrupt new registry state**

When a mutation is pending while the app switches registries with an overlapping service ID, and the old response arrives afterward, `changed()` emits only the service ID and verification, so a list handler cannot distinguish the old registry’s result from the new registry’s. It can store or delete verification state in the new registry’s map, leaving the list with incorrect verification state.

Include registry identity in the event detail and have list handlers apply results only to the matching registry.

Also at `packages/ecc-client-elixir-cloud-registry/src/events/ecc-service-verification-changed.ts:5-7`, `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:386`.
</issue_to_address>

### Comment 2
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="224-229" />
<code_context>
+    this.loadRequest += 1;
+    const request = this.loadRequest;
+    const id = this.targetId;
+    if (this.verification !== undefined) {
+      const { own } = this;
+      this.current =
+        own && this.showsSame(own.id, own.provider)
+          ? own.verification
+          : this.verification;
+      return;
+    }
</code_context>
<issue_to_address>
**Old registry badge appears on new registry**

When the provider changes to a different registry while `verification` still contains a value loaded from the previous registry, `load()` treats any defined `verification` as authoritative without associating it with a registry. If the provider switches from registry A to registry B before the app replaces that property, the component skips B's GET and shows A's matching verification as B's, so users see a false Verified badge.

Scope supplied verifications to their registry and discard or reload them when the component switches to a different registry.
</issue_to_address>

### Comment 3
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="186" />
<code_context>
+      changedProperties.has("baseUrl") ||
+      changedProperties.has("serviceId") ||
+      changedProperties.has("verification") ||
+      (changedProperties.has("service") && previous?.id !== this.service?.id)
+    ) {
+      this.load();
</code_context>
<issue_to_address>
**Stale verification reappears as verified**

When the service’s URL or API type changes and later returns to its previously verified values without changing its ID, `updated()` skips `load()` when the service changes but keeps the same ID, leaving `current` unchanged. When the service returns to its old values, `matchesVerification()` matches that stale result and the component shows “Verified” even though the registry removed the verification.

Reload or clear `current` whenever the service changes, including changes to its URL or API type.

Also at `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:227-229`.
</issue_to_address>

### Comment 4
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="340" />
<code_context>
+      this.applied(id, verification, provider);
+    } catch (err) {
+      this.setPending(id, false);
+      this.fail(failureDetail(id, "verify", err, "Failed to verify service"));
+      if (
+        statusOf(err) === 409 &&
</code_context>
<issue_to_address>
**Mutation failures leave stale state**

When a verify or remove is applied by the registry but the client receives a timeout, connection failure, or error response, `verifyService()` and `removeVerification()` treat every non-conflict/non-404 rejection as a failed mutation without reconciling state. If the registry applied the change but the response was lost or errored, the component and any event-driven app map keep showing the old verification state.

Reconcile the verification after ambiguous mutation failures, while still reporting the failed request.

Also at `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:373`.
</issue_to_address>

### Comment 5
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="301" />
<code_context>
+        this.own = { id, verification, provider };
+      }
+    }
+    this.changed(id, verification);
+  }
+
</code_context>
<issue_to_address>
**Older mutation overwrites newer state**

When separate component instances or tabs mutate the same service and responses arrive in a different order from the registry’s applied changes, `applied()` emits each mutation result as it arrives, with no ordering or version check. If two instances mutate the same service and the registry applies PUT then DELETE but the responses arrive in reverse order, an event-driven consumer can apply the old PUT result last and show the service as verified when it is not.

Reconcile state after mutation events or include and compare a server ordering/version value before applying results.
</issue_to_address>

### Comment 6
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="298-299" />
<code_context>
+      this.loadRequest += 1;
+      this.current = verification;
+      if (this.verification !== undefined) {
+        this.own = { id, verification, provider };
+      }
+    }
+    this.changed(id, verification);
</code_context>
<issue_to_address>
**Successful mutation result disappears**

When a verify or remove succeeds with `verification` undefined, then the provider is replaced for the same registry, `applied()` does not save the result in `own`, and `load()` clears `current` before refetching. A 404, failed, or stale refetch then hides the result of the successful mutation.

Save successful mutation results in `own` even when `verification` is undefined, so `load()` can reuse them after a provider change.
</issue_to_address>

### Comment 7
<location path="packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts" line_range="163" />
<code_context>
+
+  /** Whether a verify or remove of the service shown is running. */
+  private get busy(): boolean {
+    return this.pending.has(this.targetId);
+  }
+
</code_context>
<issue_to_address>
**Old registry request disables new actions**

When a request is pending while the provider switches to another registry with the same service ID, `pending` and `busy` use only the service ID, so the old registry’s request keeps Verify or Remove disabled for the new registry until it settles. A hung request leaves the new registry’s actions disabled indefinitely.

Key pending work by both registry and service ID, and have `busy` check the current registry’s key.

Also at `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:164`, `packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:277-280`.
</issue_to_address>

### Comment 8
<location path="apps/documentation/pages/docs/cloud-registry/components/service-verification.mdx" line_range="120" />
<code_context>
+After a verify or remove, the component shows the result for that service until the app sets `verification` again, also if the provider is replaced meanwhile by one for the same registry (the same base URL, e.g. with a new token); update your verifications from `ecc-service-verification-changed`, as above. Pass a stable value (e.g. the object from the map, not one built on each render): every new value counts as set again.
+
+<Callout type='info'>
+  The services list of the GA4GH Service Registry package (`ecc-client-ga4gh-service-registry-services`) doesn't show verifications itself; place the component in a row's `actions-<id>` slot or in your own list.
+</Callout>
+
</code_context>
<issue_to_address>
**Badge missing from list**

The issue asks for a verified badge in the services list, but this change adds only a standalone component and says the list does not show verifications itself. Apps must wire the component into a row manually, so the list requirement remains partly met.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 8 findings to address first, and an admin can create a persistent verification record that the component displays as a badge, so a wrong verification could mislead users even after this code is reverted. The record is bounded and can be removed through the registry, but reverting the component alone does not remove it.

Blocking findings: packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:301, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:229, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:186, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:340, packages/ecc-client-elixir-cloud-registry/src/components/service-verification/service-verification.ts:301, and 3 more


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Address automated review: load the verification again when the URL or
API type of the service changes and after a verify or remove fails
without a clear outcome (no status or a server error), and key
running requests by registry and service, so that a request to one
registry doesn't block another.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(cloud-registry): verifications

1 participant